/**
 * @Description:
 * @version: 1.0.0
 * @Author: YinJianFeng
 * @Date: 2024-03-29 10:15:27
 */

import {
  Constraint,
  DocumentEvents,
  DocumentObject,
  DocumentObjectsTree,
  DocumentObjectTreeNodeType,
  frameUtils,
  getDefaultNodeIcon,
  Icon,
  MenuItemCopy,
  MenuItemPaste,
  ObjInfo,
  PickFrom,
  sketchRootNodeTypes,
  sketchUtils,
  STATE,
  Types,
  useClipboard,
} from '@amcax/base';
import { Picker2D, Picker3D, PickerMgr } from '@amcax/renderer';
import { MeasureEvents } from '@app-base/common/utils/measure/events';
import { measureRenderManager } from '@app-base/manager/MeasureRenderManager';
import { PartLeftPanelEvents } from '@app-cad-env/nextcad/widgets/LeftPanel/types';
import getEditCommand from '@app-cad/common/config/getEditCommand';
import { CAD_EVENT, COMMON_EVENTS } from '@app-cad/common/Constants';
import { csEvent } from '@app-cad/common/events/EventCenter';
import { invokeEditCommand } from '@app-cad/common/utils';
import { constraintIconPicker } from '@app-cad/common/utils/sketchTools/ConstraintIconPicker';
import {
  ConstraintData,
  ConstraintIconEvents,
} from '@app-cad/common/utils/sketchTools/ConstraintIconRenderer';
import { ctx2, docMgr, userEvents } from '@app-cad/module';
import { useMemoizedFn } from 'ahooks';
import { TreeNodeProps } from 'antd';
import type RcTree from 'rc-tree';
import {
  ComponentType,
  Dispatch,
  FC,
  ReactNode,
  SetStateAction,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';
import styled from 'styled-components';
import { DefaultContextMenu } from '../ContextMenu/DefaultContextMenu';
import { MenuItemDelete } from '../ContextMenu/DefaultContextMenu/delete';
import { ContextMenuEvents } from '../ContextMenu/DefaultContextMenu/events';
import { RelationsEvents } from '../ContextMenu/DefaultContextMenu/RelationsDialog/types';
import {
  nonVisibilityTypes,
  setVisibility,
} from '../ContextMenu/DefaultContextMenu/visibility-menu-items';
import { HideIconComponent, ShowIconComponent } from './images';

const StyledTreeWrapper = styled.div`
  /* 重写 antd Tree 图标容器css */

  .ant-tree-iconEle.ant-tree-icon__customize {
    width: auto;
    display: inline-flex;
    align-items: center;
    justify-content: flex-start;
  }
`;

// 为避免CADIcons改动，使用高阶组件包裹CADIcons
function WithVisibilityFunc<T extends { data?: DocumentObject }>(
  WrappedComponent: ComponentType<T>,
): FC<T> {
  const ComponentWithHiddenIcon: FC<T> = (props) => {
    const { visibility, type } = props.data;
    const isNonVisibilityTypes = nonVisibilityTypes.includes(props.data.type);
    if (isNonVisibilityTypes) {
      return <WrappedComponent {...props} />;
    }
    return (
      <div className='flex items-center w-10 h-6'>
        <span className='w-4 h-4 flex items-center justify-center flex-shrink-0'>
          <>
            {visibility && !nonVisibilityTypes.includes(type) && (
              <ShowIconComponent
                onClick={() => {
                  setVisibility([props.data], false);
                }}
              />
            )}
            {!visibility && !nonVisibilityTypes.includes(type) && (
              <HideIconComponent
                onClick={() => {
                  setVisibility([props.data], true);
                }}
              />
            )}
          </>
        </span>
        <span className='ml-1 flex items-center justify-center overflow-hidden'>
          <WrappedComponent {...props} />
        </span>
      </div>
    );
  };

  ComponentWithHiddenIcon.displayName = `WithVisibilityFunc(${WrappedComponent.displayName || WrappedComponent.name || 'Component'})`;

  return ComponentWithHiddenIcon;
}

const DirectTypeIconMap = {
  [Types.DESIGN_TABLE_CONFIGURATION]:
    'commands/common/design-table-configuration',
};

export const CADIcons = (props) => {
  if (DirectTypeIconMap[props.data.type]) {
    return <Icon name={DirectTypeIconMap[props.data.type]} />;
  }

  const documentObject = props.data as unknown as ObjInfo;
  const command = getEditCommand(documentObject);

  if (command?.command?.icon == null || command.notApplyIconToLeftPanel) {
    return (getDefaultNodeIcon(ctx2) as (props: TreeNodeProps) => ReactNode)(
      props,
    );
  }

  return <Icon name={command?.command?.icon} />;
};

// 导出带有隐藏图标的CADIcons
export const CADIconsWithVisibility = WithVisibilityFunc(CADIcons);

export const LeftPanel = () => {
  const [selectedDocumentObject, setSelectedDocumentObject] =
    useState<DocumentObject>();
  const clipBoardData = useClipboard();

  const lastMouseButtonRef = useRef<number | null>(null);
  const treeRef = useRef<RcTree<any>>(null);

  useEffect(() => {
    const abortController = new AbortController();
    window.addEventListener(
      'mousedown',
      (e: MouseEvent) => (lastMouseButtonRef.current = e.button),
      abortController,
    );

    return () => abortController.abort();
  }, []);

  const listeners = useMemo(() => {
    return [
      {
        eventName: ConstraintIconEvents.SelectFromCanvas,
        handler: (
          { constraints }: { constraints: ConstraintData[] },
          setSelectedKeys: Dispatch<SetStateAction<string[]>>,
        ) => {
          setSelectedKeys(
            constraints.map((c) =>
              sketchUtils.getConstraintId(docMgr.getCurrentSketchDxid(), c),
            ),
          );
        },
      },
      {
        eventName: MeasureEvents.SelectFromCanvas,
        handler: (
          id: string,
          setSelectedKeys: Dispatch<SetStateAction<string[]>>,
        ) => {
          setSelectedKeys(id == null ? [] : [id]);
        },
      },
      {
        eventName: COMMON_EVENTS.CLICK_NULL,
        handler: (_, setSelectedKeys: Dispatch<SetStateAction<string[]>>) => {
          setSelectedKeys([]);
        },
      },
      {
        eventName: RelationsEvents.ClickObjects,
        handler: (
          objs: DocumentObject[],
          setSelectedKeys: Dispatch<SetStateAction<string[]>>,
        ) => {
          if (objs && objs.length > 0) {
            handleNodeSelected(objs, objs[0]);
            setSelectedKeys(objs.map((o) => o.dxid));
          }
        },
      },
    ];
  }, []);

  const handleNodeSelected = (
    nodes: DocumentObject[],
    newNode: DocumentObject,
  ) => {
    if (lastMouseButtonRef.current === 0) {
      userEvents.emit(DocumentEvents.SELECT_TREE_OBJECT, newNode, true);
      if (nodes.length > 1) {
        userEvents.emit(CAD_EVENT.SELECT_TREE_OBJECTS, nodes, true);
      }
    }

    setSelectedDocumentObject(newNode);

    if (sketchUtils.isSketchCurveChildId(newNode.id)) {
      const picker2D = PickerMgr.getPicker<Picker2D>('2d');
      picker2D.clickNull();
      picker2D.selectModels(
        nodes
          .filter((n) => sketchUtils.isSketchCurveChildId(n.id))
          .map((n) => sketchUtils.getCurveIdsFromCurveChildDxid(n.id)[1]),
      );
      return;
    }

    if (sketchUtils.isSketchConstraintChildId(newNode.id)) {
      const picker2D = PickerMgr.getPicker<Picker2D>('2d');
      picker2D.clickNull();
      constraintIconPicker.selectByData(
        nodes
          .filter((n) => sketchUtils.isSketchConstraintChildId(n.id))
          .map(
            (n) =>
              (n as DocumentObjectTreeNodeType)
                .originalObj as unknown as Constraint,
          ),
      );
      return;
    }

    if (docMgr.measureResult.some((m) => m.dxid === newNode.dxid)) {
      measureRenderManager.selectById(newNode.dxid);
    }

    const picker3D = PickerMgr.getPicker<Picker3D>('3d');
    picker3D
      .clearSelectedModel()
      .clearSelectedMesh()
      .clearSelectedLine()
      .clearSelectedVertex();

    if (csEvent().state === STATE.DEFAULT) {
      picker3D.selectModels(
        nodes
          .filter((n) => !sketchUtils.isSketchCurveChildId(n.id))
          .map((n) => n.dxid),
        undefined,
        PickFrom.ObjectTree,
      );
    }
  };

  const menuData = (event) => {
    return {
      props: {
        documentObject: selectedDocumentObject,
        selectedObjects: [selectedDocumentObject],
      },
      data: { clipBoardData, ctx2 },
      event: event,
      triggerEvent: undefined,
    };
  };

  const onCopy = useMemoizedFn((event) => {
    if (!selectedDocumentObject || MenuItemCopy.hidden(menuData(event))) {
      return;
    }
    MenuItemCopy.onClick(menuData(event));
  });

  const onPaste = useMemoizedFn(async (event) => {
    if (
      !selectedDocumentObject ||
      MenuItemPaste.hidden(menuData(event)) ||
      MenuItemPaste.disabled(menuData(event))
    ) {
      return;
    }
    MenuItemPaste.onClick(menuData(event));
  });

  const onDelete = useMemoizedFn(async (event) => {
    if (!selectedDocumentObject || MenuItemDelete.hidden(menuData(event))) {
      return;
    }
    MenuItemDelete.onClick(menuData(event));
    setSelectedDocumentObject(null);
  });

  const onScrollTo = useMemoizedFn((key: string) => {
    if (key) {
      treeRef.current?.scrollTo({ key });
    }
  });

  useEffect(() => {
    userEvents
      .on(ContextMenuEvents.Copy, onCopy)
      .on(ContextMenuEvents.Paste, onPaste)
      .on(ContextMenuEvents.Delete, onDelete)
      .on(PartLeftPanelEvents.ScrollTo, onScrollTo);
    return () => {
      userEvents
        .off(ContextMenuEvents.Copy, onCopy)
        .off(ContextMenuEvents.Paste, onPaste)
        .off(ContextMenuEvents.Delete, onDelete)
        .off(PartLeftPanelEvents.ScrollTo, onScrollTo);
    };
  }, []);

  return (
    <StyledTreeWrapper>
      <DocumentObjectsTree
        defaultMenuId={DefaultContextMenu.id}
        allowRightClick={(node) =>
          !frameUtils.isFrameAxesChildDxid(node.dxid) &&
          !frameUtils.isFramePlaneChildDxid(node.dxid) &&
          !sketchRootNodeTypes.includes(node.type) &&
          !sketchUtils.isSketchAxesChildDxid(node.dxid) &&
          node.type !== Types.MeasureResultRoot &&
          node.type !== Types.PARAMETER_LINK_ROOT &&
          node.type !== Types.DESIGN_TABLE_CONFIGURATION
        }
        handleSelect={handleNodeSelected}
        handleDoubleClick={invokeEditCommand}
        nodeIcon={CADIconsWithVisibility}
        listeners={listeners}
        treeRef={treeRef}
      />
    </StyledTreeWrapper>
  );
};
